# encoding: utf-8
"""
APP脱壳
@Author:      "June"
@CreateTime:  2020/5/30 18:21 
@File:        unshell
@Project:     PyCharm
"""
import sys

import frida

package_name = 'com.tianyancha.skyeye'

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

open_memory = '_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_'

"""
openMemory在libart.so文件中， art虚拟机（安卓5）， davlink虚拟机（安卓4）
Hook openMemory的导出方法
用ida打开libart.so查看openMemory的导出方法名
openMemory的第一个参数是dex文件的起始内存位置
根据dex文件格式，从起始位置开始，第32个字节是该dex文件的大小
知道dex文件的起始位置和大小，只需要把该段内存的内容dump出来即可
适用于安卓6 7 8 9
"""
src = """
    var open_memory_address = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_");
    Interceptor.attach(open_memory_address, {
        onEnter: function(args) {
            //dex文件的起始位置
            var dex_begin_address = args[1];
            
            //dex前8个字段是magic字段， 看dex文件的格式说明
            //打印magic会显示（dex 035）, 三个字符可以验证是否为dex文件
            console.log("magic: " + Memory.readUtf8String(dex_begin_address));
            
            //把地址转换成整数，再加32
            //因为dex文件的第32个字节处存放的是该dex文件的大小
            var address = parseInt(dex_begin_address, 16) + 32;
            
            //把address地址指向的内存读出来，该值就是dex文件的大小
            //ptr(address)转换的原因是，frida只接受NativePointer类型的指针
            var dex_size = Memory.readInt(ptr(address));
            console.log("dex_size: " + dex_size);
            
            //frida写文件，把内存中的数据写到本地
            var timestamp = new Date().getTime();
            var file = new File("/data/data/%s/" + timestamp + ".dex", "wb");
            
            //Memory.readByteArray(begin, length)
            //把内存中的数据读出来，从begin开始，读length长度
            file.write(Memory.readByteArray(dex_begin_address, dex_size));
            file.flush();
            file.close();
            
            send("dex begin address: " + dex_begin_address);
            send("dex file size:" + dex_size);
        },
        onLeave: function(retval) {
            if (retval.toInt32() > 0) {
            }
        }
    })
""" % package_name

print("dex导出目录为： /data/data/%s/" % package_name)
device = frida.get_usb_device()
pid = device.spawn(package_name)
session = device.attach(pid)
script = session.create_script(src)
script.on('message', on_message)
script.load()
device.resume(pid)
sys.stdin.read()